﻿using System;
using System.Reflection;
using System.Reflection.Emit;

namespace MyBasicLib.Reflection.FastReflection.Emit
{
    public class ConstructorInvoker : IConstructorInvoker
    {
        private readonly Func<object[], object> m_invoker;

        // public ConstructorInfo ConstructorInfo { get; private set; }

        public ConstructorInvoker(ConstructorInfo constructorInfo)
        {
            //      this.ConstructorInfo = constructorInfo;
            m_invoker = InitializeInvoker(constructorInfo);
        }

        #region IConstructorInvoker Members

        object IConstructorInvoker.Invoke(params object[] parameters)
        {
            return Invoke(parameters);
        }

        #endregion

        private static Func<object[], object> InitializeInvoker(ConstructorInfo constructorInfo)
        {
            Type t = constructorInfo.DeclaringType;

            var dynMethod = new DynamicMethod(string.Empty, typeof (object),
                                              new[] {typeof (object[])}, t);


            ILGenerator ilGen = dynMethod.GetILGenerator();
            ParameterInfo[] ps = constructorInfo.GetParameters();
            for (int i = 0; i < ps.Length; i++)
            {
                ilGen.Emit(OpCodes.Ldarg_0);
                ilGen.EmitFastInt(i);
                ilGen.Emit(OpCodes.Ldelem_Ref);
                ilGen.EmitCastToReference(ps[i].ParameterType);
            }
            ilGen.Emit(OpCodes.Newobj, constructorInfo);
            //  ilGen.BoxIfNeeded(constructorInfo.);
            ilGen.Emit(OpCodes.Ret);

            return dynMethod.CreateDelegate(typeof (Func<object[], object>)) as Func<object[], object>;
        }

        public object Invoke(params object[] parameters)
        {
            return m_invoker(parameters);
        }
    }
}